package com.cgs.query.remote.netty

import com.cgs.query.exception.QueryException
import com.cgs.query.handler.HELLO_METHOD
import com.cgs.query.handler.RemoteHandler
import com.cgs.query.handler.Request
import com.cgs.query.handler.Response
import mu.KotlinLogging
import java.net.InetSocketAddress
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger

private val logger = KotlinLogging.logger { }

class NettyRemoteHandler(
        private val urls: Array<String>,
        private val serialization: Serialization,
        private val timeout: Long) : RemoteHandler {

    private val clientHolder = ConcurrentHashMap<InetSocketAddress, NettyClient>()
    private val addresses = urls.map { string2InetSocketAddress(it) }.toCollection(ArrayList())
    private val index = AtomicInteger(1)

    override fun hello(appName: String) {
        transport(Request(-1L, HELLO_METHOD, listOf(appName), String::class.java))
    }

    override fun transport(request: Request): Response {
        return asyncTransport(request).get(timeout, TimeUnit.SECONDS)
    }

    override fun asyncTransport(request: Request): Future<Response> {
        val responseFuture = ResponseFuture(request)
        while (addresses.isNotEmpty()) {
            val address = addresses.route()
            try {
                getNettyClientFromCache(address).send(request)
                return responseFuture
            } catch (e: Exception) {
                e.printStackTrace()
                addresses.remove(address)
                logger.error { "连接地址无效: $address , 正在尝试其他选择" }
            }
        }
        throw QueryException("所有连接地址无效：${Arrays.toString(urls)} ")
    }

    override fun urlTransport(url: String, request: Request): Response {
        return asyncUrlTransport(url, request).get(timeout, TimeUnit.SECONDS)
    }

    override fun asyncUrlTransport(url: String, request: Request): Future<Response> {
        val responseFuture = ResponseFuture(request)
        try {
            getNettyClientFromNew(string2InetSocketAddress(url)).send(request)
            return responseFuture
        } catch (e: Exception) {
            throw QueryException("连接地址无效: $url", e)
        }
    }

    override fun getRemoteType() = "netty"

    private fun List<InetSocketAddress>.route(): InetSocketAddress {
        return this[index.getAndIncrement() % this.size]
    }

    private fun getNettyClientFromCache(address: InetSocketAddress): NettyClient {
        return clientHolder.computeIfAbsent(address) {
            NettyClient(it, serialization).apply { start() }
        }
    }

    private fun getNettyClientFromNew(address: InetSocketAddress): NettyClient {
        return NettyClient(address, serialization).apply {
            start()
        }
    }

    private fun string2InetSocketAddress(address: String): InetSocketAddress {
        val split = address.split(":")
        return if (split.size == 2) {
            val port = split[1].toIntOrNull()
            checkPort(port)
            InetSocketAddress(split[0], port!!)
        } else {
            throw QueryException("地址错误:$address")
        }
    }

    private fun checkPort(port: Int?): Int {
        if (port == null || port < 0 || port > 0xFFFF)
            throw QueryException("端口错误:$port")
        return port
    }
}